﻿using System;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Diagnostics;
using Newtonsoft.Json;

namespace QbservableCore.Serialization.Helpers
{
    [DataContract]
    public class SerializableMethodBase
    {
        [DataMember]
        private int _metadataToken; // cache key

        [DataMember]
        private TypeRepresentation[] _genArgs; 
        [DataMember]
        private TypeRepresentation _type; 

        public MethodBase Method {get; private set;}

        [Obsolete("Should be used on deserialization", true)]
        public SerializableMethodBase()
        { 
        }
        public SerializableMethodBase(MethodBase methodBase)
        {
            Method = methodBase;
        }

        #region Hook on serialization and deserialization

        #region OnSerializing

        /// <summary>
        /// Called when [serializing].
        /// </summary>
        /// <param name="context">The context.</param>
        [OnSerializing]
        public void OnSerializing(StreamingContext context)
        {
            _type = new TypeRepresentation(Method.DeclaringType);
            _metadataToken = Method.MetadataToken;
            if (Method.IsGenericMethod)
            {
                var args = from t in Method.GetGenericArguments()
                        select new TypeRepresentation(t);
                if (args.Any())
                    _genArgs = args.ToArray();
            }
        }

        #endregion // OnSerializing

        #region OnDeserialized

        /// <summary>
        /// Called when [deserialized].
        /// </summary>
        /// <param name="context">The context.</param>
        [OnDeserialized]
        public void OnDeserialized(StreamingContext context)
        {
            Type declaringType = _type.InnerType;
            TypeInfo typeInfo = declaringType.GetTypeInfo();
            Method = declaringType.Module.ResolveMethod(_metadataToken);
            if (typeInfo.IsGenericType)
            {
                if (Method is ConstructorInfo)
                {
                    Method =
                        (from ctor in typeInfo.DeclaredConstructors
                         where ctor.MetadataToken == _metadataToken
                         select ctor).First();
                }
                else // method is MethodInfo
                {
                    Method =
                        (from method in typeInfo.DeclaredMethods
                         where method.MetadataToken == _metadataToken
                         select method).First();
                }
            }

            if (_genArgs != null)
            {
                var args =  from arg in _genArgs
                                   select arg.InnerType;
                Method = ((MethodInfo)Method).MakeGenericMethod(args.ToArray());
            }

        }

        #endregion // OnDeserialized

        #endregion // Hook on serialization and deserialization
    }

}